/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.monkey;

import com.intellij.ide.util.EditSourceUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataConstants;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;

import javax.swing.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class FindCallerAction extends AnAction {

    public void actionPerformed(AnActionEvent anActionEvent) {
        DataContext datacontext = anActionEvent.getDataContext();
        final Project project = (Project) datacontext.getData(DataConstants.PROJECT);
        if (project == null) {
            System.out.println("no project");
            return;
        }
        PsiDocumentManager.getInstance(project).commitAllDocuments();
        PsiElement element = (PsiElement) datacontext.getData(DataConstants.PSI_ELEMENT);
        System.out.println("element1 = " + element);
        if (element == null) {
            return;
        }
        Editor editor = (Editor) datacontext.getData(DataConstants.EDITOR);
        List elements = new ArrayList();
        checkCaller(element, project, elements);
        if (element instanceof PsiMethod) {
            PsiMethod psiMethod = (PsiMethod) element;
            System.out.println("psiMethod = " + psiMethod);
            System.out.println("psiMethod.isConstructor() = " + psiMethod.isConstructor());
            PsiMethod[] psiMethods = psiMethod.findSuperMethods();
            for (int i = 0; i < psiMethods.length; i++) {
                PsiMethod method = psiMethods[i];
                checkCaller(method, project, elements);
            }
        }
        navigate(editor, (PsiElement[]) elements.toArray(new PsiElement[0]));
    }

    private void checkCaller(PsiElement element, Project project, List elements) {
        boolean testSource = isTestSource(element);
        SearchScope searchScope = getSearchScope(testSource, project);
        Collection<PsiReference> all = ReferencesSearch.search(element, searchScope, true).findAll();
        for (PsiReference psiReference : all) {
            if (psiReference instanceof PsiElement) {
                elements.add(psiReference);
            }
        }
    }

    private SearchScope getSearchScope(boolean testSource, Project project) {
        if (testSource) {
            return GlobalSearchScope.projectTestScope(project, false);
        } else {
            return GlobalSearchScope.projectProductionScope(project, false);
        }
    }

    private boolean isTestSource(PsiElement element) {
        PsiFile psiFile = element.getContainingFile();
        ProjectRootManager rootManager = ProjectRootManager
                .getInstance(psiFile.getProject());
        ProjectFileIndex fileIndex = rootManager.getFileIndex();
        VirtualFile virtualFile = psiFile.getVirtualFile();
        return virtualFile != null && fileIndex.isInTestSourceContent(virtualFile);
    }

    private static void navigate(Editor editor, PsiElement[] options) {
        if (options == null || options.length == 0) {
            return;
        }
        if (options.length == 1) {
            Navigatable navigatable = EditSourceUtil.getDescriptor(options[0]);
            if (navigatable != null && navigatable.canNavigate()) {
                navigatable.navigate(true);
            }
        } else {
            ReferenceCellRenderer cellRenderer = new ReferenceCellRenderer();
            Arrays.sort(options, cellRenderer.getComparator());
            final JList jlist = new JList(options);
            jlist.setCellRenderer(cellRenderer);
            cellRenderer.installSpeedSearch(jlist);
            Runnable runnable = new Runnable() {

                public void run() {
                    int[] selectedItems = jlist.getSelectedIndices();
                    if (selectedItems == null || selectedItems.length == 0) {
                        return;
                    }
                    int length = jlist.getSelectedValues().length;
                    for (int l = 0; l < length; l++) {
                        Object obj1 = jlist.getSelectedValues()[l];
                        Navigatable navigatable1 = EditSourceUtil.getDescriptor((PsiElement) obj1);
                        if (navigatable1 != null && navigatable1.canNavigate()) {
                            navigatable1.navigate(true);
                        }
                    }
                }
            };
            String title = "Choose caller to open";
            (new PopupChooserBuilder(jlist)).setTitle(title).setItemChoosenCallback(runnable).createPopup().showInBestPositionFor(editor);
        }
    }
}

